//
//  NSMHTMLController.m
//  iShow
//
//  Created by nsm on 2018/7/5.
//  Copyright © 2018年 nani. All rights reserved.
//

#import "NSMHTMLController.h"

@implementation WKProcessPool (SharedProcessPool)
+ (WKProcessPool *)sharedProcessPool {
    static WKProcessPool *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [WKProcessPool new];
    });
    return instance;
}
@end

static NSString * const kScriptMessageName = @"sendMessage";

@interface NSMHTMLController () <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>
@property (nonatomic, strong) NSArray<NSString *> *keyPaths;
@property (nonatomic, strong) CAShapeLayer *loadingLayer;
@property (nonatomic) BOOL isPushed;
@end

@implementation NSMHTMLController

#pragma mark - lifeCycle
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [_wkWebView.configuration.userContentController addScriptMessageHandler:self name:kScriptMessageName];
    // 由js控制是否刷新登录状态
    [self runJSFuncInitLogin];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    if (self.removePreviousController) {
        NSMutableArray *vcs = self.navigationController.viewControllers.mutableCopy;
        if (vcs.count < 2) return;
        
        [vcs removeObjectAtIndex:vcs.count-2];
        self.navigationController.viewControllers = vcs;
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [_wkWebView.configuration.userContentController removeScriptMessageHandlerForName:kScriptMessageName];
}

#pragma mark - override
- (void)didInitialize {
    [super didInitialize];
    
    self.keyPaths = @[@"estimatedProgress", @"title", @"canGoBack"];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    self.wkWebView.frame = self.view.bounds;
}

#pragma mark - initialization
- (void)viewDidLoad {
    [super viewDidLoad];
    
    _shareEnabled = NO;
    if (_shareEnabled) {
        NSMButton *btn = NSMButton.new;
        btn.wrapContentSize = YES;
        btn.imageSize = CGSizeMake(20, 18);
        btn.spacing = 2;
        btn.imagePosition = NSMButtonImagePositionTop;
        btn.titleLabel.font = CustomFont(11);
        btn.adjustsWhenHighlighted = NO;
        [btn setTitleColor:SUB_TEXT_COLOR forState:UIControlStateNormal];
        [btn setTitleColor:UIColor.redColor forState:UIControlStateSelected];
        [btn setImage:[[NameImage(@"global_heart_n") imageByTintColor:SUB_TEXT_COLOR] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal];
        [btn setImage:[[NameImage(@"global_heart_s") imageByTintColor:UIColor.redColor] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateSelected];
        btn.title = @"收藏";
        [btn addTarget:self action:@selector(onClickHeart:) forControlEvents:UIControlEventTouchUpInside];
        
        UIBarButtonItem *heartItem = [[UIBarButtonItem alloc] initWithCustomView:btn];
        self.navigationItem.rightBarButtonItem = heartItem;
        
        [_networker POST:@"article/get_detail.html" parameters:@{@"article_id": SafeString(_shareID)} success:^(NSMResponseObject *responseObject) {
            btn.selected = [responseObject.data[@"is_collect"] isEqual:@"1"];
        } empty:nil failure:nil hud:NO];
    }
    
    [self initWebView];
    [self resetObserver];
    
    [self loadRequest:self.htmlURL];
}

- (void)initWebView {
    WKUserContentController *uc = [WKUserContentController new];
    // 放到viewWillAppear以防止循环引用
    // [uc addScriptMessageHandler:self name:kScriptMessageName];
    
    WKWebViewConfiguration *cfg = [WKWebViewConfiguration new];
    // 共享Cookie
    // cfg.processPool = [WKProcessPool sharedProcessPool];
    cfg.userContentController = uc;
    cfg.allowsInlineMediaPlayback = YES;
    
    WKPreferences *pre = [WKPreferences new];
    pre.javaScriptCanOpenWindowsAutomatically = YES;
    cfg.preferences = pre;
    
    WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds
                                              configuration:cfg];
    wkWebView.UIDelegate = self;
    wkWebView.navigationDelegate = self;
    wkWebView.allowsBackForwardNavigationGestures = NO;
    [self.view addSubview:wkWebView];
    
    self.wkWebView = wkWebView;
}

- (void)resetObserver {
    // 移除所有的KVO
    [self.KVOController unobserveAll];
    
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew;
    @nsm_weak(self)
    [self.KVOController observe:self.wkWebView keyPaths:[self whiteListForKVO] options:options block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        @nsm_strong(self)
        NSString *key = change[FBKVONotificationKeyPathKey];
        if ([key isEqual:@"estimatedProgress"]) {
            
        } else if ([key isEqual:@"title"]) {
            self.navigationItem.title = SafeString(change[NSKeyValueChangeNewKey]);
        } else if ([key isEqual:@"canGoBack"]) {
            
        }
    }];
}

#pragma mark - WKNavigationDelegate
/**
 1.发送请求之前决定是否跳转
 
 decisionHandler(WKNavigationActionPolicyAllow)
 -> 执行2(didStartProvisionalNavigation)
 
 decisionHandler(WKNavigationActionPolicyCancel)
 -> 所有代理中断. 多用来拦截跳转
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *urlString = navigationAction.request.URL.absoluteString;
    NSLog(@"\nHTML链接:\n%@\n", urlString);
    WKNavigationActionPolicy policy = WKNavigationActionPolicyAllow;
    NSArray *schemes = SafeArray(InfoPlistValueForKey(@"LSApplicationQueriesSchemes"));
    BOOL isNative = NO;
    for (NSString *scheme in schemes) {
        NSString *totalScheme = FormatString(@"%@://", scheme);
        if ([urlString hasPrefix:totalScheme]) {
            isNative = YES;
            break;
        }
    }
    if (isNative) {
        policy = WKNavigationActionPolicyCancel;
        OpenURL(urlString);
    }
    decisionHandler(policy);
}

/**
 2.页面开始加载
 
 不管有没有网络
 -> 执行3(decidePolicyForNavigationResponse)
 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    [self startLoadingAnimation];
}

/**
 3.收到响应之后决定是否跳转
 
 decisionHandler(WKNavigationResponsePolicyAllow);
 成功->执行4(didCommitNavigation)
 失败->执行4(didFailProvisionalNavigation)
 
 decisionHandler(WKNavigationResponsePolicyCancel);
 ->执行4(didFailProvisionalNavigation)
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 4.页面内容加载失败
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    [self stopLoadingAnimation];
}

/**
 4.页面内容加载成功,开始返回内容
 
 成功->执行5(didFinishNavigation)
 失败->执行5(didFailNavigation)
 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    [self stopLoadingAnimation];
}

// 5.内容返回成功
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [self stopLoadingAnimation];
}

// 5.内容返回失败
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    [self stopLoadingAnimation];
}

#pragma mark - WKUIDelegate
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    [self pushHTMLController:nil URLString:navigationAction.request.URL.absoluteString animated:YES];
    return nil;
}

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    [self showAlert:message action:^(UIAlertAction *action) {
        completionHandler();
    }];
}

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if (![message.name isEqual:kScriptMessageName]) return;
    
    // NSNumber, NSString, NSDate, NSArray, NSDictionary, NSNull类型
    [self router:message.body];
}

#pragma mark - JS和OC互相调用
- (void)router:(id)obj {
    if (![obj isKindOfClass:[NSDictionary class]]) return;
    
    /**
     cmd NSNumber 命令码(确定跳转功能)
     1. 支付
     2. 返回并刷新上一级
     3. 登录
     4. 读取通讯录(添加收货地址联系人)
     5. 跳转下一级并关闭当前页
     */
    id cmd = obj[@"cmd"];
    if ([cmd isEqual:@1]) {
    }
    else if ([cmd isEqual:@2]) {
        NSArray *vcs = self.navigationController.viewControllers;
        NSMHTMLController *vc = (NSMHTMLController *)vcs[vcs.count-2];
        [vc.wkWebView reload];
        
        [self.navigationController popViewControllerAnimated:YES];
    }
    else if ([cmd isEqual:@3]) {
        @nsm_weak(self)
        [self presentLoginController:^(LoginController *vc) {
            @nsm_strong(self)
            vc.didLoginSuccess = ^(id data) {
                [self runJSFuncInitLogin];
            };
        } animated:YES completion:nil];
    }
    else if ([cmd isEqual:@4]) {
    }
    else if ([cmd isEqual:@5]) {
        [self pushHTMLController:^(NSMHTMLController *vc) {
            vc.removePreviousController = YES;
        } URLString:obj[@"url"] animated:YES];
    };
}

- (void)runJSFuncInitLogin {
    /**
     JS调用OC
     window.webkit.messageHandlers.kScriptMessageName.postMessage(body)
     OC调用JS
     NSString *jsString = @"funcname('arg1','arg2')";
     [self.wkWebView evaluateJavaScript:jsString completionHandler:nil];
     */
    NSLog(@"JS登录方法");
    NSString *uid = _appCache.userInfo.userID;
    NSString *token = _appCache.userInfo.token;
    NSString *jsString = FormatString(@"initLogin('%@','%@')", uid, token);
    [self.wkWebView evaluateJavaScript:jsString completionHandler:nil];
}

- (void)runJSFuncGetBook:(NSString *)name mobile:(NSString *)phoneNumber {
    NSString *jsString = FormatString(@"getBook('%@','%@')", name, phoneNumber);
    [self.wkWebView evaluateJavaScript:jsString completionHandler:nil];
}

#pragma mark - setter/getter
- (void)setBlackListForKVO:(NSArray<NSString *> *)blackListForKVO {
    _blackListForKVO = blackListForKVO;
    
    if (!self.wkWebView) return;
    [self resetObserver];
}

- (NSArray<NSString *> *)whiteListForKVO {
    NSMutableArray<NSString *> *keyPaths = self.keyPaths.mutableCopy;
    for (NSString *blackKeyPath in self.blackListForKVO) {
        [keyPaths removeObject:blackKeyPath];
    }
    return keyPaths;
}

- (CAShapeLayer *)loadingLayer {
    if (!_loadingLayer) {
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.geometryFlipped = YES;
        layer.fillColor = COLOR_5.CGColor;
        layer.strokeColor = RGBColor(230, 230, 230).CGColor;
        layer.lineWidth = 2;
        layer.lineJoin = kCALineJoinRound;
        _loadingLayer = layer;
    }
    return _loadingLayer;
}

#pragma mark - network

#pragma mark - method/public

#pragma mark - method/private
- (void)loadRequest:(id)request {
    NSURLRequest *urlRequest;
    if ([request isKindOfClass:[NSString class]])
        urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:request]];
    else if ([request isKindOfClass:[NSURL class]])
        urlRequest = [NSURLRequest requestWithURL:request];
    else if ([request isKindOfClass:[NSURLRequest class]])
        urlRequest = request;
    
    if (!urlRequest) return;
    
    NSMutableURLRequest *mutRequest = urlRequest.mutableCopy;
    // [self setCookieWithRequest:mutRequest];
    [self.wkWebView loadRequest:mutRequest];
}

- (UIBezierPath *)bezierPathWithText:(NSString *)text attributes:(NSDictionary *)attrs {
    NSAssert(text!= nil && attrs != nil, @"参数不能为空");
    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:text attributes:attrs];
    CGMutablePathRef paths = CGPathCreateMutable();
    CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
    CFArrayRef runArray = CTLineGetGlyphRuns(line);
    for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) {
        CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
        CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
        for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) {
            CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
            CGGlyph glyph;
            CGPoint position;
            CTRunGetGlyphs(run, thisGlyphRange, &glyph);
            CTRunGetPositions(run, thisGlyphRange, &position);
            {
                CGPathRef path = CTFontCreatePathForGlyph(runFont, glyph, NULL);
                CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
                CGPathAddPath(paths, &t,path); CGPathRelease(path);
            }
        }
    }
    CFRelease(line);
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointZero];
    [path appendPath:[UIBezierPath bezierPathWithCGPath:paths]];
    CGPathRelease(paths);
    
    return path;
}

- (void)startLoadingAnimation {
    if (!self.loadingLayer.superlayer) {
        [self.view.layer addSublayer:self.loadingLayer];
    }
    
    [self.loadingLayer removeAllAnimations];
    
    CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    ani.fromValue = @(0.1);
    ani.toValue = @(1.0);
    ani.duration = 2.0;
    ani.repeatCount = HUGE_VALF;
    ani.removedOnCompletion = NO;
    
    UIBezierPath *path = [self bezierPathWithText:@"GD"
                                       attributes:@{NSFontAttributeName : CustomFont(24)}];
    self.loadingLayer.size = CGPathGetBoundingBox(path.CGPath).size;
    self.loadingLayer.center = CGPointMake(self.view.width/2, self.view.height/2);
    self.loadingLayer.path = path.CGPath;
    [self.loadingLayer addAnimation:ani forKey:@"wk_loading"];
}

- (void)stopLoadingAnimation {
    if (self.loadingLayer.superlayer) {
        [self.loadingLayer removeFromSuperlayer];
    }
    [self.loadingLayer removeAllAnimations];
}

#pragma mark - Cookie
/**
 关键字:WKHTTPCookieStore, NSHTTPCookieStorage, WKProcessPool
 https://github.com/xtcmoons/WKWebView-Cookie
 */
- (void)setCookieWithRequest:(NSMutableURLRequest *)request {
    if (!request) return;
    
    NSString *uid = SafeString(_appCache.userInfo.userID);
    NSString *token = SafeString(_appCache.userInfo.token);
    
    // 是否自动发送Cookie
    request.HTTPShouldHandleCookies = YES;
    // 方式一:多个cookie之间用分号+空格分隔开 (cookie1=value1; cookie2=value2)
    NSString *cookieString = FormatString(@"uid=%@; token=%@", uid, token);
    [request setValue:cookieString forHTTPHeaderField:@"Cookie"];
    
    // 方式二:NSHTTPCookie对象, 参考NSHTTPCookiePropertyKey
    /*
    NSHTTPCookie *cookieUid = [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName: @"uid",
                                                                   NSHTTPCookieValue: uid}];
    NSHTTPCookie *cookieToken = [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName: @"token",
                                                                     NSHTTPCookieValue: token}];
    NSArray *cookies = @[cookieUid, cookieToken];
    NSDictionary *headerFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    request.allHTTPHeaderFields = headerFields;
     */
}

#pragma mark - action
- (BOOL)backBarButtonItemShouldPopBack {
    if ([self.wkWebView canGoBack])
        [self.wkWebView goBack];
    else
        [self.navigationController popViewControllerAnimated:YES];
    
    return NO;
}

- (void)onClickHeart:(NSMButton *)sender {
    NSDictionary *param = @{@"link_id": SafeString(_shareID),
                            @"link_type": @"1",
                            @"is_collect": sender.isSelected?@"2":@"1"};
    [_networker POST:@"collect/collect.html" parameters:param success:^(NSMResponseObject *responseObject) {
        sender.selected = !sender.isSelected;
    } empty:nil failure:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

@implementation UIViewController (NSMHTMLController)
- (void)pushHTMLController:(void (^)(NSMHTMLController *))hander URLString:(NSString *)urlString animated:(BOOL)animated {
    if (!urlString.length) return;
    
//    if ([urlString hasSuffix:kToken]) {
//        // 是否检查登录后缀
//        BOOL isCheckSuffix = NO;
//        if (!_appCache.isLogin) {
//            if (isCheckSuffix) {
//                // 未登录前往登录
//                [self presentLoginController:nil animated:YES completion:nil];
//                return;
//            }
//        } else {
//            // 已登录替换url
//            urlString = [urlString stringByReplacingOccurrencesOfString:kUid withString:_appCache.userInfo.userID];
//            urlString = [urlString stringByReplacingOccurrencesOfString:kToken withString:_appCache.userInfo.token];
//        }
//    }
    
    NSMHTMLController *vc = [NSMHTMLController new];
    vc.htmlURL = urlString;
    vc.hidesBottomBarWhenPushed = YES;
    !hander?:hander(vc);
    [self.navigationController pushViewController:vc animated:animated];
}
@end


